/*
 * Copyright (c) 2020 Fraunhofer FOKUS and others. All rights reserved.
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contact: mosaic@fokus.fraunhofer.de
 */

package org.eclipse.mosaic.app.enterprise.obu;

import com.google.common.collect.Lists;
import org.eclipse.mosaic.fed.application.app.AbstractApplication;
import org.eclipse.mosaic.fed.application.app.api.MqttApplication;
import org.eclipse.mosaic.fed.application.app.api.os.VehicleOperatingSystem;
import org.eclipse.mosaic.fed.mqtt.protocol.ObuProtocol;
import org.eclipse.mosaic.interactions.mqtt.MqttPublish;
import org.eclipse.mosaic.interactions.out.ObuFileOutputUpdates;
import org.eclipse.mosaic.lib.objects.vehicle.VehicleData;
import org.eclipse.mosaic.lib.util.JsonUtils;
import org.eclipse.mosaic.lib.util.scheduling.Event;
import org.eclipse.mosaic.rti.TIME;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * On Board Unit Application
 * Sending messages of positions and accelerate every fixed seconds
 */
public class OnBoardUnitApp extends AbstractApplication<VehicleOperatingSystem> implements MqttApplication {

    private static final Logger log = LoggerFactory.getLogger(OnBoardUnitApp.class);

    @Override
    public void onStartup() {
        getLog().infoSimTime(this, "Initial application");
        firstSample();
    }

    public void firstSample() {
        getOs().getEventManager().addEvent(new Event(
                getOs().getSimulationTime() + 100 * TIME.MILLI_SECOND, this::sendMq));
    }

    public void sendMq(Event event) {
        if (!canProcessEvent()) {
            return;
        }

        ObuProtocol protocol = generateData();
        // publish(MqttTopic.VEHICLE_MOVE.getTopic(), protocol);
        ObuFileOutputUpdates updates = new ObuFileOutputUpdates(getOs().getSimulationTime(),
                Lists.newArrayList(JsonUtils.serialize(protocol)));
        getOs().sendInteractionToRti(updates);

        getOs().getEventManager().addEvent(new Event(
                getOs().getSimulationTime() + 100 * TIME.MILLI_SECOND, this::sendMq));
    }

    public ObuProtocol generateData() {
        VehicleData vehicleData = getOs().getNavigationModule().getVehicleData();

        // failsafe
        if (vehicleData == null) {
            return null;
        }

        // unit -> second
        long realTime = System.currentTimeMillis();

        ObuProtocol.ObuBody body = new ObuProtocol.ObuBody.Builder()
                .setVehicleId(vehicleData.getName())
                .setPosition(new ObuProtocol.Position(vehicleData.getPosition().getLatitude(), vehicleData.getPosition().getLongitude(), 5352d))
                .setTimestampGNSS(realTime)
                .setAccelerationLon(vehicleData.getLongitudinalAcceleration())
                .setHeading(vehicleData.getHeading())
                .setVelocityCAN(vehicleData.getSpeed())
                .defaultValue().build();


        return new ObuProtocol(new ObuProtocol.ObuHeader(), body);
    }

    @Override
    public void onShutdown() {

    }

    @Override
    public void processEvent(Event event) throws Exception {
        // nop
    }

    @Override
    public void publish(String topic, Object payload) {
        MqttPublish publish = new MqttPublish(getOs().getSimulationTime(), topic, payload, 0, Boolean.FALSE);

        getOs().sendInteractionToRti(publish);
    }
}
